版权声明:本文为博主原创文章,转载请注明出处:http://blog.jerkybible.com/2015/06/04/Spring MVC 之 RequestMapping(2)/
一、从头开始
SpringMvc会通过DispatcherServlet来处理所有的请求,展开这个方法。
下面展开getHandler是怎么做的:
以上两个方法都是RequestMappingHandlerMapping的祖先类,逻辑很简单,先获取当前请求的路径,然后查找该路径对应的HandlerMethod实例。@RequestMapping (1)
这篇博客最后讲到了,RequestMappingHandlerMapping中的两个映射表实例,urlMap和handlerMethods,第一个是路径与RequestMappingInfo的映射,第二个是RequestMappingInfo和HandlerMethod的映射,lookupHandlerMethod方法负责检索这两个变量了:
在进行URL匹配中,Spring会先查找是否存在直接匹配的RequestMappingInfo实例,即@RequestMapping中的value,method属性完全匹配请求的,如果没有找到通常是存在PathVariable的,如果/{no}和/222的情况等也是匹配的, 找到匹配项后,需要找出最优解,然后将路径中的变量存入Request的变量表中,我们分别详细的了解下:
我们知道RequestMappingInfo就是@RequestMapping注解的抽象,它包含@RequestMapping中的所有属性,因此在查找匹配项时,需要查看所有这些属性是否与请求匹配。我们这里只看路径模式是否匹配,都很简单:
至于上面的pathMatcher.match方法这里就不分析了,可以自己看看,匹配算法还是比较复杂的,主要是尽可能的全面,除了进行匹配外,还会将路径中的变量保存起来以便@PathVariable参数使用。
以上便是整个的匹配过程,好麻烦,或许你会说这会不会降低Spring的性能?实话说,在处理首次请求时,效率是很差,但是Spring使用了各种缓存策略,一旦程序进入正轨,效率就非常高了。
二、处理器方法的调用
现在我们已经查找到了对应请求的处理器方法,下面我们就看Spring是如何在运行时动态地调用处理器方法的,并传递正确的参数。在doDispatch方法中,我们看到,确定了处理器(方法)后,Spring接着获取了该处理器方法的适配器(HandlerAdapter概念讲解中说到过,用来调用处理器方法的)
|
|
获取了HandlerAdapter后,Spring就会调用handlerAdapter实例的handle方法,并返回ModelAndView实例:
我们接着看invokeHandlerMethod方法:
关于异步调用链那块我们暂不关心,。从上面代码可以看到,在调用方法前,分别检查了处理器中存在的@InitBinder注解的方法和@ModelAttribute注解的方法,InitBinder方法用于类型转化,如将String转化为Date类型等,可以通过@InitBinder方法实现,感兴趣可以自己看看,不在详细分析。至于@ModelAttribute注解的方法,其返回值会被放入Model对象中供视图使用。下面我们看invokeAndHandle方法:
接着我们看下invokeForRequest方法:
啊哈上面的代码貌似很简单,实则不是,重点就在getMethodArgumentValues方法:
该方法是HandlerMethod中的方法,因此可以调用getMethodParameters()方法获取参数列表,然后遍历这些参数,分别用参数解析器来解析当前参数值,其中,argumentResolvers是HandlerMethodArgumentResolverComposite,它包含了所有的参数解析器的列表,以及参数类型和解析器的映射表,我们不妨看看到底什么怎么回事:
这下清晰了吧,RequestMappingHandlerAdapter实现了InitializingBean接口,因此Spring启动是会调用它的afterPropertySet方法,进行上述参数解析器的注册。然后在处理器方法调用过程中会遍历这些解析器找到支持当前参数的解析器并解析参数。Perfect。我们再回到之前的resolveArgument方法:
再往下就到某个解析器怎样解析具体参数了,有大约一二十个解析器,我们不可能全部分析,这里我们只分析其中常见的一个:@PathVariable注解的解析器。
从上面代码我们知道PathVariableMethodArgumentResolver支持被@PathVariable注解的参数。下面我们看它怎样解析参数值得:
上面代码首先获取参数的@PathVariable的value属性值,如果value是空,则将参数的名称作为NameValueInfo的name值,然后用这个name值匹配请求路径中的变量值,作为NameValueInfo的value值。
|
|
上面再lookupHandlerMethod方法中,调用了handleMatch方法,我们说了它会将解析到的路径变量放到request的变量表中,这不,这里就用到了,这样我们就获取到了@PathVariable对应参数的值了。到此为止,@PathVariable参数的解析就算完成了,其他类型的参数解析思路一样不同的就是resolveArgument方法中的逻辑了,大家可以自行了解。
注:这里Spring用到了多种设计模式,包括组合模式,策略模式,适配器模式等。其实这些实现都是3.1v的,之前的版本,参数解析这块相当乱,几乎完全在一个方法内实现的,拓展性,维护性相当差,3.1后我们可以很轻松的实现自己的参数解析器等,真的很棒。
三、返回值的处理
上一节我们分析了参数的解析,及方法的调用,下面我们再来看返回值的处理,回到HandlerMethod的invokeAndHandle方法来,该方法的最后调用了returnValueHandlers.handleReturenValue方法,其中returnValueHandlers是HandlerMethodReturnValueHandlerComposite实例,就像HandlermethodArgumentResolverComposite一样,它包含了所有HandlerMethodReturnValueHandler的列表,并在Spring启动时完成注册。
|
|
返回值的处理思路与参数的处理几乎一样了,根据不同的返回值类型,查找匹配的处理器,然后进行处理(主要就是设置Model和View了,如上面的代码将返回值解析为视图名),这里就不多说了。返回值处理完了,剩下的就是将返回值响应给客户端了,再往下就是视图的解析了。